`timescale 1ns/100ps

module dcm_444_to_422_filt
#(parameter
    DATAPATH_WIDTH = 8,
    VIDEO_SIZE_BW = 11
)
(
    input   I_sclk,
    input   I_rst_n,
    input   I_dv,
    
    input   [ DATAPATH_WIDTH - 1: 0] I_data,
    input   I_de,
    input   I_hsync,
    input   I_vsync,

    output  reg [ DATAPATH_WIDTH - 1: 0] O_data,
    output  O_de,
    output  O_hsync,
    output  O_vsync
);

reg  [ DATAPATH_WIDTH - 1: 0] data_r;
reg  [ DATAPATH_WIDTH - 1: 0] data_d1;
reg  [ DATAPATH_WIDTH - 1: 0] data_d2;
reg  [ DATAPATH_WIDTH - 1: 0] data_d3;
reg  [ DATAPATH_WIDTH - 1: 0] data_d4;
reg  [ DATAPATH_WIDTH - 1: 0] data_d5;

reg  de_r;
reg  de_d1;
reg  de_d2;
reg  de_d3;
reg  de_d4;
reg  de_d5;

reg  hsync_r;
reg  hsync_d1;
reg  hsync_d2;
reg  hsync_d3;
reg  hsync_d4;
reg  hsync_d5;

reg  vsync_r;
reg  vsync_d1;
reg  vsync_d2;
reg  vsync_d3;
reg  vsync_d4;
reg  vsync_d5;

wire de_pos;
wire de_neg;

reg  [ DATAPATH_WIDTH: 0] sum15;
reg  [ DATAPATH_WIDTH: 0] sum24;
reg  [ DATAPATH_WIDTH + 1: 0] d3_mul3;
wire [ DATAPATH_WIDTH + 3: 0] result;

assign O_hsync = hsync_d5;
assign O_vsync = vsync_d5;
assign O_de    = de_d5;

assign de_pos = de_d1 & !de_d2; 
assign de_neg = !de_d1 & de_d2;

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        begin
        data_r  <= 0;
        data_d1 <= 0;
        data_d2 <= 0;
        data_d3 <= 0;
        data_d4 <= 0;
        data_d5 <= 0;
        end
    else if ( I_dv )
        begin
        data_r <= I_data;
        if ( de_pos )
            begin
            data_d1 <= data_r;
            data_d2 <= data_d1;
            data_d3 <= data_d1;
            data_d4 <= data_d1;
            data_d5 <= data_d1;
            end
        else if ( !de_r )
            begin
            data_d1 <= data_d1;
            data_d2 <= data_d1;
            data_d3 <= data_d2;
            data_d4 <= data_d3;
            data_d5 <= data_d4;
            end
        else
            begin
            data_d1 <= data_r;
            data_d2 <= data_d1;
            data_d3 <= data_d2;
            data_d4 <= data_d3;
            data_d5 <= data_d4;
            end
        end

// 5 taps filter [1/16,1/4,3/8,1/4,1/16]
always @(posedge I_sclk)
    begin
    sum15   <= data_d1 + data_d5;
    sum24   <= data_d2 + data_d4;
    d3_mul3 <= {data_d3,1'b0} + data_d3; // *3
    end

assign result = sum15 + {sum24,2'b00} + {d3_mul3,1'b0};

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        O_data <= 0;
    else if ( I_dv )
        O_data <= de_d4 ? result[DATAPATH_WIDTH+3 : 4] : {DATAPATH_WIDTH{1'b0}};

always @(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        begin
        de_r     <= 0;
        de_d1    <= 0;
        de_d2    <= 0;
        de_d3    <= 0;
        de_d4    <= 0;
        de_d5    <= 0;
        hsync_r  <= 0;
        hsync_d1 <= 0;
        hsync_d2 <= 0;
        hsync_d3 <= 0;
        hsync_d4 <= 0;
        hsync_d5 <= 0;
        vsync_r  <= 0;
        vsync_d1 <= 0;
        vsync_d2 <= 0;
        vsync_d3 <= 0;
        vsync_d4 <= 0;
        vsync_d5 <= 0;
        end
    else if ( I_dv )
        begin
        de_r     <= I_de;
        de_d1    <= de_r;
        de_d2    <= de_d1;
        de_d3    <= de_d2;
        de_d4    <= de_d3;
        de_d5    <= de_d4;
        hsync_r  <= I_hsync;
        hsync_d1 <= hsync_r;
        hsync_d2 <= hsync_d1;
        hsync_d3 <= hsync_d2;
        hsync_d4 <= hsync_d3;
        hsync_d5 <= hsync_d4;
        vsync_r  <= I_vsync;
        vsync_d1 <= vsync_r;
        vsync_d2 <= vsync_d1;
        vsync_d3 <= vsync_d2;
        vsync_d4 <= vsync_d3;
        vsync_d5 <= vsync_d4;
        end

endmodule

